package com.legendframework.core.log;

import com.legendframework.core.log.enums.LoggerLevelEnum;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 196
 * @Description: 日志类
 * @date 2019/12/11 17:28
 */
public abstract class AbstractLegendLogger implements Logger {

    public AbstractLegendLogger(String serviceCode){
        this.serviceCode = serviceCode;
    }

    //服务名称
    protected String serviceCode;

    /**
     * 获取当前日志级别
     * @return
     */
    abstract LoggerLevelEnum getLoggerLevel();

    /**
     * 获取日志输出模板格式
     * @return
     */
    abstract String getTemplateInfo();

    /**
     * 获取当前调用该方法的模块名称
     * 例如: ROLE
     *      LEVEL
     */
    protected String getModelName(){
        return serviceCode;
    }

    /**
     * 判断该级别与当前日志级别的权重输出
     * @param loggerLevelEnum 日志等级
     * @return 是否输出
     */
    protected boolean isLevel(LoggerLevelEnum loggerLevelEnum){
        return getLoggerLevel().getLevel() >= loggerLevelEnum.getLevel();
    }

    /**
     * 输出日志信息
     * @param log 信息
     * @param loggerLevel 日志级别
     * @param args 参数
     */
    private void logByLevel(String log , LoggerLevelEnum loggerLevel , Object... args){
        String callerClassName = getCallerClassName();
        if (isLevel(loggerLevel)) {
            Throwable throwable = null;
            if (args.length > 0) {
                //判断最后一个参数是否是异常类
                Object checkExp = args[args.length-1];
                if (checkExp instanceof Throwable) {
                    //数组剪掉最后一个元素
                    Object[] objs = new Object[args.length -1];
                    System.arraycopy(args,0,objs,0,args.length - 1);
                    args = objs;
                    throwable = (Throwable) checkExp;
                }
            }
            write(loggerLevel,callerClassName,String.format(log,args));
            if (throwable != null) {
                System.err.println(getStackTrace(throwable));
            }
        }
    }

    /**
     * debug日志
     * @param log 日志信息
     * @param args 参数
     */
    @Override
    public void debug(String log , Object... args){
        logByLevel(log,LoggerLevelEnum.DEBUG,args);
    }

    /**
     * warn日志
     * @param log 日志信息
     * @param args 参数
     */
    @Override
    public void warn(String log , Object... args){
        logByLevel(log,LoggerLevelEnum.WARN,args);
    }

    /**
     * info日志
     * @param log 日志信息
     * @param args 参数
     */
    @Override
    public void info(String log , Object... args){
        logByLevel(log,LoggerLevelEnum.INFO,args);
    }

    /**
     * error日志
     * @param log 日志信息
     * @param args 参数
     */
    @Override
    public void error(String log , Object... args){
        logByLevel(log,LoggerLevelEnum.ERROR,args);
    }


    /**
     * 获取调用这个方法的那个方法,是被哪个类所调用的
     *
     * 注意: 该方法只能被日志级别所调用,特定方法
     *
     * @return 全限定类名类名
     */
    protected String getCallerClassName(){
        StackTraceElement stack[] = (new Throwable()).getStackTrace();
        if (stack == null || stack.length < 4) {
            return "";
        }
        return stack[3].getClassName();
    }

    /**
     * 输出日志
     * @param loggerLevel 日志级别
     * @param callerClassName 全限定类名
     * @param info 信息
     */
    protected void write(LoggerLevelEnum loggerLevel , String callerClassName , Object info){
        String writeInfo = variableSubstitution(loggerLevel,callerClassName,new Date(),info);
        switch (loggerLevel){
            case ERROR:
                System.err.println(writeInfo);
                break;
            default:
                System.out.println(writeInfo);
        }
    }

    /**
     * 日志变量转换
     * @param loggerLevel 日志级别
     * @param callerClassName 全限定类名
     * @param date 日期
     * @param info 信息
     * @return 转换后格式的日志信息
     */
    protected String variableSubstitution(LoggerLevelEnum loggerLevel ,String callerClassName , Date date , Object info){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");
        String writeInfo = getTemplateInfo().replace(LogVariableEnum.SERVICE.getName(),getModelName());
        writeInfo = writeInfo.replace(LogVariableEnum.LOG_LEVEL.getName(),loggerLevel.name());
        writeInfo = writeInfo.replace(LogVariableEnum.CLASS_NAME.getName(),callerClassName);
        writeInfo = writeInfo.replace(LogVariableEnum.DATE_TIME.getName(),sdf.format(date));
        return writeInfo.replace(LogVariableEnum.INFO.getName(),String.valueOf(info));
    }

    /**
     * 日志变量
     */
    public enum LogVariableEnum {
        //服务名
        SERVICE("#SERVICE#"),
        //日志级别
        LOG_LEVEL("#LOG_LEVEL#"),
        //全限定类名
        CLASS_NAME("#CLASS_NAME#"),
        //日期
        DATE_TIME("#DATE_TIME#"),
        //输出信息
        INFO("#INFO#");

        private String name;

        LogVariableEnum(String name){
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    /**
     * 输出异常信息堆栈信息
     * @param throwable
     * @return
     */
    public static String getStackTrace(Throwable throwable)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);

        try {
            throwable.printStackTrace(pw);
            return sw.toString();
        } finally {
            pw.close();
        }
    }

}
